#include "code_highlighter.h"
#include <QSyntaxHighlighter>
#include <QRegularExpressionMatchIterator>

CodeHighLighter::CodeHighLighter(QTextDocument* parent) : QSyntaxHighlighter(parent)
{

    HighlightingRule rule;

    // 1. 关键字
    // 正则表达式中 \b 代表单词的边界
    const QString keywordPatterns[] = {"\\bchar\\b",    "\\bclass\\b",     "\\bconst\\b",   "\\bdouble\\b",   "\\benum\\b",      "\\bexplicit\\b",
        "\\bfriend\\b",  "\\binline\\b",    "\\bint\\b",     "\\blong\\b",     "\\bnamespace\\b", "\\boperator\\b",
        "\\bprivate\\b", "\\bprotected\\b", "\\bpublic\\b",  "\\bshort\\b",    "\\bsignals\\b",   "\\bsigned\\b",
        "\\bslots\\b",   "\\bstatic\\b",    "\\bstruct\\b",  "\\btemplate\\b", "\\btypedef\\b",   "\\btypename\\b",
        "\\bunion\\b",   "\\bunsigned\\b",  "\\bvirtual\\b", "\\bvoid\\b",     "\\bvolatile\\b",  "\\bbool\\b"
    };
    keywordFormat.setForeground(Qt::darkBlue);
    keywordFormat.setFontWeight(QFont::Bold);
    keywordFormat.setFontFamily("Consolas");

    for(QString pattern : keywordPatterns)
    {
        rule.pattern = QRegularExpression(pattern);
        rule.format = keywordFormat;
        highlightingRules.push_back(rule);
    }

    // 2. QT类
    rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b");  // +表示匹配前面的表达式至少1个。等价于`{1,}`。
    classFormat.setFontWeight(QFont::Bold);
    classFormat.setForeground(Qt::darkMagenta);
    classFormat.setFontFamily("Consolas");
    rule.format = classFormat;
    highlightingRules.push_back(rule);

    // 3. 数字
    //    rule.pattern = QRegularExpression("[+-]{0,1}[0-9]{0,}\\.{0,1}[0-9]{0,}");
    rule.pattern = QRegularExpression("[+-]?\\d*\\.?\\d*");  // 上面写法的精简写法
    numberFormat.setForeground(QColor(255, 147, 13));
    numberFormat.setFontFamily("Consolas");
    rule.format = numberFormat;
    highlightingRules.push_back(rule);

    // 4. 函数
    rule.pattern = QRegularExpression("\\b[A-Za-z0-9_]+(?=\\()");  // 正向预查。只要后边有(，则前面的函数名匹配成功
    functionFormat.setFontItalic(true);
    functionFormat.setForeground(Qt::blue);
    functionFormat.setFontFamily("Consolas");
    rule.format = functionFormat;
    highlightingRules.push_back(rule);

    // 5. 字符串--就是双引号括起来的内容
    rule.pattern = QRegularExpression("\".*\"");  // 除换行外的任意字符，出现0次或多次。
    quotationFormat.setForeground(Qt::darkGreen);
    quotationFormat.setFontFamily("Consolas");
    rule.format = quotationFormat;
    highlightingRules.push_back(rule);

    // 6. 单行注释--以两个斜杠开始的单行
    singleLineCommentFormat.setForeground(QColor(120, 120, 120));
    singleLineCommentFormat.setFontFamily("Consolas");
    rule.pattern = QRegularExpression("//[^\n]*");  // 除换行外的任意字符，出现0次或多次。这里也可以写做 "//[.]*"
    rule.format = singleLineCommentFormat;
    highlightingRules.push_back(rule);

    // 7. 多行注释 也就是被 /* 和 */ 包裹的内容
    multiLineCommentFormat.setForeground(QColor(36, 128, 0));
    multiLineCommentFormat.setFontFamily("Consolas");
    commentStartExpression = QRegularExpression("/\\*");  // 要表示*需要使用两个反斜杠
    commentEndExpression = QRegularExpression("\\*/");


}

void CodeHighLighter::highlightBlock(const QString &text)
{
    static int i = 1; //参数QTextedit中的每一行

    //1、按照顺序匹配：关键字，qt类.....
    for(HighlightingRule rule : highlightingRules)
    {
        QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
        while(matchIterator.hasNext())
        {
            QRegularExpressionMatch match = matchIterator.next();
            setFormat(match.capturedStart(),match.capturedLength(),rule.format);
        }
    }

    //2 匹配多行注释
    setCurrentBlockState(0);//0 不在多行注解里面，1 在多上注解里面
    int startIndex;
    if(previousBlockState() == 1)
    {
        startIndex = 0;//如果当前行已经在注释中，开始查询结束标识
    }else{
        startIndex = text.indexOf(commentStartExpression);//未找到开始标识 返回-1
    }

    while(startIndex >= 0){
        QRegularExpressionMatch match = commentEndExpression.match(text,startIndex);
        int endIndex = match.capturedStart();
        int commentLength = 0;
        if(endIndex == -1)
        {
            //未找到结束标识
            setCurrentBlockState(1);
            commentLength = text.length()-startIndex;
        }
        else{
            //找到结束标识符
            // commentLength = endIndex - startIndex + 2;  // 2 代表字符串 "*/" 的长度
            commentLength = endIndex - startIndex + match.capturedLength();
        }

        setFormat(startIndex,commentLength,multiLineCommentFormat);

        startIndex = text.indexOf(commentStartExpression,startIndex+commentLength);

    }
}














